package com.gitee.ed.pattern.chain;

import com.gitee.ed.common.CommonUtils;
import com.gitee.ed.common.Priority;

import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

/**
 * @author hongda.li
 */
public class ChainBuilder<Context> {
    /**
     * 头节点
     */
    private Chain<Context> head;
    /**
     * 尾节点
     */
    private Chain<Context> tail;

    /**
     * 构建一个普通的链式构建器
     *
     * @param <Context> 上下文泛型
     * @return 链式构建器
     */
    public static <Context> ChainBuilder<Context> newBuilder() {
        return new ChainBuilder<>();
    }

    /**
     * 根据链式处理器集合构建一个链式构建器
     * 并根据链式处理器的优先级顺序构建
     * 优先级的顺序由 @Priority 注解指定{@link com.gitee.ed.common.Priority}
     * 若链式处理器不存在 @Priority 注解 则按默认顺序构建
     *
     * @param <Context> 上下文泛型
     * @return 链式构建器
     */
    public static <Context> ChainBuilder<Context> newBuilder(List<Chain<Context>> chainList) {
        return newBuilder(chainList, chain -> CommonUtils.parsePriority(chain.getClass()));
    }

    /**
     * 根据链式处理器集合构建一个链式构建器
     * 并由调用者显式指定是否对链式构建器按优先级排序
     * 优先级的顺序由 @Priority 注解指定{@link com.gitee.ed.common.Priority}
     *
     * @param <Context> 上下文泛型
     * @return 链式构建器
     */
    public static <Context> ChainBuilder<Context> newBuilder(List<Chain<Context>> chainList, Function<Chain<Context>, Integer> priorityProvider) {
        Objects.requireNonNull(chainList);
        Objects.requireNonNull(priorityProvider);

        ChainBuilder<Context> builder = new ChainBuilder<>();

        chainList.stream()
                .filter(Objects::nonNull)
                .sorted(Comparator.comparingInt(chain -> {
                    Integer priority = priorityProvider.apply(chain);
                    return Objects.requireNonNullElse(priority, Priority.NORMAL_PRIORITY);
                }))
                .forEach(builder::nextChain);

        return builder;
    }

    /**
     * 链式构建
     *
     * @param chain 链式处理器
     * @return 链式调用
     */
    public ChainBuilder<Context> nextChain(Chain<Context> chain) {
        if (this.head == null) {
            this.head = this.tail = chain;
        } else {
            this.tail.setNext(chain);
            this.tail = this.tail.getNext();
        }
        return this;
    }

    /**
     * 构建最终链结构
     * 返回头节点
     *
     * @return 最终链式结构
     */
    public Chain<Context> build() {
        return this.head;
    }
}
